"""
调度任务模块，用于定时执行告警查询和推送任务
"""
import os
import time
import json
import threading
from datetime import datetime
from typing import List, Dict, Any, Optional, Callable
import sys

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger

from src.utils.config import config_manager
from src.utils.logger import logger
from src.database.mysql import mysql_manager
from src.wechat.message import wechat_message


class TaskScheduler:
    """任务调度器，负责定时执行告警查询和推送任务"""
    
    def __init__(self):
        """初始化任务调度器"""
        self.scheduler = BackgroundScheduler()
        self.is_running = False
        self.job_id = "alarm_check"
        self.minute_list = config_manager.get_value("scheduler", "check_minutes", "0,15,30,45")  # 默认每15分钟检查一次
        self.resend_daily_alarms = config_manager.get_value("scheduler", "resend_daily_alarms", False)  # 是否重复发送当日消息
        
        # 重启时间初始化
        self.restart_hour = 3
        self.restart_minute = 0
        
        # 已发送告警记录
        # 根据是否是打包环境，选择不同的路径
        if getattr(sys, 'frozen', False):
            # 如果是打包后的可执行文件，使用可执行文件所在目录
            base_dir = os.path.dirname(sys.executable)
            self.sent_alarms_file = os.path.join(base_dir, "logs", "sent_alarms.json")
        else:
            # 如果是开发环境，使用项目根目录
            self.sent_alarms_file = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 
                                           "logs", "sent_alarms.json")
        
        logger.debug(f"已发送告警记录文件路径: {self.sent_alarms_file}")
        self.sent_alarms = self._load_sent_alarms()
        
        # 设置调度器任务
        self._setup_scheduler()
        
        # 错误恢复相关
        self.consecutive_failures = 0  # 连续失败次数
        self.max_failures = 3  # 最大连续失败次数
        self.last_recovery_time = time.time()  # 上次恢复时间
        
        # 清理可能存在的旧重启脚本
        self._cleanup_restart_scripts()
    
    def _cleanup_restart_scripts(self):
        """清理可能存在的旧重启脚本"""
        try:
            # 获取程序目录
            if getattr(sys, 'frozen', False):
                app_dir = os.path.dirname(sys.executable)
            else:
                app_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
            
            # 检查并删除可能存在的旧重启脚本
            restart_script = os.path.join(app_dir, "restart.bat")
            if os.path.exists(restart_script):
                try:
                    os.remove(restart_script)
                    logger.info(f"已删除旧的重启脚本: {restart_script}")
                except Exception as e:
                    logger.warning(f"删除旧重启脚本失败: {e}")
        except Exception as e:
            logger.warning(f"清理旧重启脚本时出错: {e}")
    
    def setup_auto_restart(self):
        """设置自动重启机制"""
        try:
            # 添加定时重启任务 - 使用单一时间参数
            from apscheduler.triggers.cron import CronTrigger
            
            # 获取重启时间，格式为"HH:MM"，默认为"03:00"
            restart_time = config_manager.get_value("scheduler", "restart_time", "03:00")
            
            # 解析时间字符串
            try:
                if ":" in restart_time:
                    hour_str, minute_str = restart_time.split(":")
                    restart_hour = int(hour_str) % 24
                    restart_minute = int(minute_str) % 60
                else:
                    # 如果只有小时，则分钟为0
                    restart_hour = int(restart_time) % 24
                    restart_minute = 0
                    
                logger.info(f"解析后的重启时间: {restart_hour:02d}:{restart_minute:02d}")
            except (ValueError, TypeError):
                # 如果解析失败，使用默认值
                logger.warning(f"无法解析重启时间'{restart_time}'，将使用默认值03:00")
                restart_hour = 3
                restart_minute = 0
            
            # 创建触发器
            restart_trigger = CronTrigger(hour=restart_hour, minute=restart_minute)
            
            # 确保调度器已启动
            if not self.scheduler.running:
                logger.info("调度器未启动，正在启动...")
                self.scheduler.start()
            
            # 添加或更新任务
            self.scheduler.add_job(
                self._check_restart_time,
                trigger="interval",
                minutes=1,  # 每分钟检查一次
                id="check_restart_time",
                replace_existing=True
            )
            
            # 存储重启时间
            self.restart_hour = restart_hour
            self.restart_minute = restart_minute
            
            # 计算下一次重启时间
            from datetime import datetime, timedelta
            now = datetime.now()
            today = now.date()
            restart_time_today = datetime(
                year=today.year,
                month=today.month,
                day=today.day,
                hour=restart_hour,
                minute=restart_minute
            )
            
            # 如果今天的重启时间已过，则计算明天的重启时间
            if now > restart_time_today:
                next_restart = restart_time_today + timedelta(days=1)
                logger.info(f"今天的重启时间点 {restart_hour:02d}:{restart_minute:02d} 已过，下次将在明天执行")
            else:
                next_restart = restart_time_today
                time_diff = (restart_time_today - now).total_seconds() / 60
                logger.info(f"距离下次重启还有 {int(time_diff)} 分钟")
            
            logger.info(f"已设置自动重启任务，程序将在每天{restart_hour:02d}:{restart_minute:02d}自动重启")
            return True
        except Exception as e:
            logger.error(f"设置自动重启任务失败: {e}", exc_info=True)
            return False
    
    def _check_restart_time(self):
        """检查是否到达重启时间"""
        try:
            # 获取当前时间
            from datetime import datetime
            now = datetime.now()
            
            # 每分钟记录一次日志，便于排查问题
            if now.second < 10:
                logger.debug(f"检查重启时间: 当前时间 {now.hour:02d}:{now.minute:02d}，目标重启时间 {self.restart_hour:02d}:{self.restart_minute:02d}")
            
            # 如果当前小时和分钟与设置的重启时间匹配，则触发重启
            if hasattr(self, 'restart_hour') and hasattr(self, 'restart_minute'):
                if now.hour == self.restart_hour and now.minute == self.restart_minute:
                    logger.info(f"到达重启时间 {self.restart_hour:02d}:{self.restart_minute:02d}，准备重启程序...")
                    # 记录重启前的系统状态
                    logger.info(f"重启前系统状态 - 是否打包环境: {getattr(sys, 'frozen', False)}, 可执行文件路径: {sys.executable}")
                    self._restart_program()
                    return True
            return False
        except Exception as e:
            logger.error(f"检查重启时间异常: {e}", exc_info=True)
            return False
            
    def _restart_program(self):
        """重启程序"""
        try:
            logger.info("准备重启程序...")
            
            # 获取当前程序路径
            if getattr(sys, 'frozen', False):
                # 如果是打包后的可执行文件
                app_path = sys.executable
            else:
                # 如果是开发环境
                app_path = os.path.abspath(sys.argv[0])
            
            # 获取程序目录
            app_dir = os.path.dirname(app_path)
            
            # 直接使用os.system启动新程序，这是最可靠的方式
            logger.info(f"直接启动新程序: {app_path}")
            
            # 使用最简单的方式启动程序 - 直接使用start命令
            if getattr(sys, 'frozen', False):
                # 如果是打包后的可执行文件，直接使用start命令
                os.system(f'start "" "{app_path}" --auto-restarted')
            else:
                # 如果是开发环境，使用python启动
                os.system(f'start "" "{sys.executable}" "{app_path}" --auto-restarted')
            
            logger.info("新程序已启动，原程序将在5秒后退出...")
            
            # 确保日志被写入
            import logging
            for handler in logging.getLogger().handlers:
                handler.flush()
            
            # 等待较长时间再退出，确保新程序已经启动
            time.sleep(5)
            
            # 强制退出
            logger.info("原程序即将退出...")
            os._exit(0)
        except Exception as e:
            logger.error(f"重启程序失败: {e}")
            return False
            
    def _reset_wechat_instance(self):
        """重置微信实例 - 直接重启整个程序"""
        try:
            logger.info("检测到微信实例需要重置，准备重启整个程序...")
            
            # 直接调用重启程序方法
            return self._restart_program()
        except Exception as e:
            logger.error(f"重启程序失败: {e}")
            return False
    
    def _load_sent_alarms(self) -> Dict[str, int]:
        """
        加载已发送的告警记录
        
        Returns:
            Dict[str, int]: 按日期分组的最后一个已发送告警ID
        """
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(self.sent_alarms_file), exist_ok=True)
            
            # 如果文件不存在，返回空字典
            if not os.path.exists(self.sent_alarms_file):
                return {}
            
            # 读取文件
            with open(self.sent_alarms_file, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"加载已发送告警记录异常: {e}")
            return {}
    
    def _save_sent_alarms(self) -> bool:
        """
        保存已发送的告警记录
        
        Returns:
            bool: 是否成功保存
        """
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(self.sent_alarms_file), exist_ok=True)
            
            # 写入文件
            with open(self.sent_alarms_file, "w", encoding="utf-8") as f:
                json.dump(self.sent_alarms, f)
            
            return True
        except Exception as e:
            logger.error(f"保存已发送告警记录异常: {e}")
            return False
    
    def _add_sent_alarm(self, date: str, alarm_id: int) -> None:
        """
        添加已发送的告警记录 - 只保存每天的最大record_id
        
        Args:
            date: 日期，格式为YYYY-MM-DD
            alarm_id: 告警ID
        """
        # 保存该日期的最大record_id
        current_max_id = self.sent_alarms.get(date, 0)
        if alarm_id > current_max_id:
            self.sent_alarms[date] = alarm_id
            # 保存记录
            self._save_sent_alarms()
            logger.debug(f"已更新{date}的最大record_id为{alarm_id}")
    
    def _is_alarm_sent(self, date: str, alarm_id: int) -> bool:
        """
        检查告警是否已发送 - 比较record_id是否小于等于记录的最大ID
        
        Args:
            date: 日期，格式为YYYY-MM-DD
            alarm_id: 告警ID
        
        Returns:
            bool: 是否已发送
        """
        max_sent_id = self.sent_alarms.get(date, 0)
        return alarm_id <= max_sent_id
    
    def _cleanup_old_records(self, keep_days: int = 7) -> None:
        """
        清理旧的告警记录
        
        Args:
            keep_days: 保留天数
        """
        try:
            # 获取当前日期
            current_date = datetime.now()
            
            # 遍历所有日期
            dates_to_remove = []
            for date_str in self.sent_alarms.keys():
                try:
                    # 解析日期
                    record_date = datetime.strptime(date_str, "%Y-%m-%d")
                    
                    # 计算日期差
                    days_diff = (current_date - record_date).days
                    
                    # 如果超过保留天数，添加到待删除列表
                    if days_diff > keep_days:
                        dates_to_remove.append(date_str)
                except:
                    # 日期格式错误，添加到待删除列表
                    dates_to_remove.append(date_str)
            
            # 删除旧记录
            for date_str in dates_to_remove:
                del self.sent_alarms[date_str]
            
            # 保存记录
            if dates_to_remove:
                self._save_sent_alarms()
                logger.info(f"已清理{len(dates_to_remove)}天的旧告警记录")
        except Exception as e:
            logger.error(f"清理旧告警记录异常: {e}")
    
    def _setup_scheduler(self) -> None:
        """设置调度器任务"""
        # 初始化调度器
        self.scheduler = BackgroundScheduler()
        
        # 获取配置的分钟列表
        minutes = self._parse_minute_list()
        
        # 添加定时任务
        cron_trigger = CronTrigger(minute=",".join([str(m) for m in minutes]))
        self.scheduler.add_job(
            self.check_alarms,
            trigger=cron_trigger,
            id=self.job_id,
            replace_existing=True,
            max_instances=1  # 限制最大实例数为1，防止任务堆积
        )
    
    def _parse_minute_list(self) -> List[int]:
        """
        解析配置的分钟列表
        
        Returns:
            List[int]: 分钟列表
        """
        minutes = []
        try:
            if isinstance(self.minute_list, list):
                # 如果已经是列表，直接使用
                for m in self.minute_list:
                    minutes.append(int(m))
            elif isinstance(self.minute_list, str):
                # 如果是字符串，按逗号分隔
                parts = self.minute_list.split(",")
                for part in parts:
                    if part.strip():
                        minutes.append(int(part.strip()))
            
            # 去除无效值
            minutes = [m for m in minutes if 0 <= m < 60]
            
            # 如果没有有效值，使用默认值
            if not minutes:
                minutes = [0, 15, 30, 45]
        except Exception as e:
            logger.error(f"解析分钟列表异常: {e}")
            minutes = [0, 15, 30, 45]
        
        return sorted(minutes)
    
    def start(self) -> bool:
        """
        启动调度器
        
        Returns:
            bool: 是否成功启动
        """
        if self.is_running:
            logger.info("调度器已在运行")
            return True
        
        try:
            # 更新配置的分钟列表
            self.minute_list = config_manager.get_value("scheduler", "check_minutes", "0,15,30,45")
            
            # 重新设置调度器
            self._setup_scheduler()
            
            # 启动调度器
            self.scheduler.start()
            self.is_running = True
            logger.info("调度器已启动")
            return True
        except Exception as e:
            logger.error(f"启动调度器异常: {e}")
            return False
    
    def stop(self) -> bool:
        """
        停止调度器
        
        Returns:
            bool: 是否成功停止
        """
        if not self.is_running:
            logger.info("调度器未运行")
            return True
        
        try:
            # 停止调度器
            self.scheduler.shutdown()
            self.is_running = False
            logger.info("调度器已停止")
            return True
        except Exception as e:
            logger.error(f"停止调度器异常: {e}")
            return False
    
    def update_schedule(self, minute_list: str) -> bool:
        """
        更新调度计划
        
        Args:
            minute_list: 分钟列表，格式为逗号分隔的字符串，如"0,15,30,45"
        
        Returns:
            bool: 是否成功更新
        """
        try:
            # 更新分钟列表
            self.minute_list = minute_list
            config_manager.set_value("scheduler", "check_minutes", minute_list)
            config_manager.save()
            
            # 如果调度器正在运行，需要重启以应用新设置
            if self.is_running:
                self.stop()
                self.start()
            else:
                # 仅更新设置但不启动
                self._setup_scheduler()
            
            logger.info(f"调度计划已更新: {minute_list}")
            return True
        except Exception as e:
            logger.error(f"更新调度计划异常: {e}")
            return False
    
    def update_resend_setting(self, resend: bool) -> bool:
        """
        更新是否重复发送当日消息设置
        
        Args:
            resend: 是否重复发送当日消息
        
        Returns:
            bool: 是否成功更新
        """
        try:
            # 更新设置
            self.resend_daily_alarms = resend
            config_manager.set_value("scheduler", "resend_daily_alarms", resend)
            config_manager.save()
            
            return True
        except Exception as e:
            logger.error(f"更新重复发送设置异常: {e}")
            return False
    
    def _split_alarms_into_batches(self, alarms: List[Dict[str, Any]], batch_size: int = 10) -> List[List[Dict[str, Any]]]:
        """
        将告警列表分割成多个批次
        
        Args:
            alarms: 告警列表
            batch_size: 每批次的告警数量
            
        Returns:
            List[List[Dict[str, Any]]]: 分割后的告警批次列表
        """
        batches = []
        for i in range(0, len(alarms), batch_size):
            batches.append(alarms[i:i+batch_size])
        return batches
    
    def check_alarms(self) -> bool:
        """
        检测告警并发送
        
        Returns:
            bool: 是否成功检测并发送
        """
        try:
            # 记录开始时间
            start_time = datetime.now()
            logger.info(f"开始检测告警: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
            
            # 设置任务超时时间（2分钟）
            timeout_seconds = 120
            
            # 连接数据库
            if not mysql_manager.connect():
                logger.error("数据库连接失败")
                self.consecutive_failures += 1
                self._check_recovery_needed()
                return False
            
            try:
                # 查询最新告警
                table_name = config_manager.get_value("database", "table_name", "t_warning_info")
                # 修改为查询当天的所有告警
                current_date = datetime.now().strftime("%Y-%m-%d")
                
                sql = f"""
                SELECT * FROM {table_name}
                WHERE DATE(create_time) = '{current_date}'
                ORDER BY create_time DESC
                """
                results = mysql_manager.query(sql)
                
                if not results:
                    logger.info("未发现符合条件的今日告警")
                    # 成功执行，重置失败计数
                    self.consecutive_failures = 0
                    return True
                
                # 重新加载已发送告警记录，确保获取最新状态
                self.sent_alarms = self._load_sent_alarms()
                
                # 过滤掉已发送的告警（如果不重复发送）
                alarms_to_send = results
                if not self.resend_daily_alarms:
                    new_alarms = []
                    max_sent_id = self.sent_alarms.get(current_date, 0)
                    logger.info(f"当前日期{current_date}的最大已发送record_id为{max_sent_id}")
                    
                    for alarm in results:
                        alarm_id = alarm.get('record_id')
                        if alarm_id and alarm_id > max_sent_id:
                            new_alarms.append(alarm)
                    
                    # 如果没有新告警，直接返回
                    if not new_alarms:
                        logger.info("今日告警已全部发送")
                        # 成功执行，重置失败计数
                        self.consecutive_failures = 0
                        return True
                    
                    alarms_to_send = new_alarms
                    logger.info(f"发现{len(new_alarms)}条新告警")
                else:
                    # 如果重复发送，仍然记录已发送告警，但不过滤
                    logger.info(f"发现{len(results)}条告警(包含已发送)")
                
                # 按warning_set_id分组告警
                grouped_alarms = self._group_alarms_by_warning_set_id(alarms_to_send)
                logger.info(f"告警按warning_set_id分组后共{len(grouped_alarms)}组")
                
                # 发送告警消息
                success_count = 0
                total_groups = len(grouped_alarms)
                total_contacts = 0  # 总联系人数
                success_contacts = 0  # 成功发送的联系人数
                
                # 追踪所有告警ID，无论发送是否成功都要记录
                all_alarm_ids = []
                
                for warning_set_id, alarms in grouped_alarms.items():
                    # 检查是否超时
                    elapsed_time = (datetime.now() - start_time).total_seconds()
                    if elapsed_time > timeout_seconds:
                        logger.error(f"告警处理超时，已处理{success_count}/{total_groups}组，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                        # 记录已处理的最大告警ID
                        if all_alarm_ids:
                            max_alarm_id = max(all_alarm_ids)
                            self._add_sent_alarm(current_date, max_alarm_id)
                            logger.info(f"已记录当日已处理的最大告警ID: {max_alarm_id}")
                        
                        # 如果成功发送了部分告警，不计入连续失败
                        if success_count > 0:
                            self.consecutive_failures = 0
                        else:
                            self.consecutive_failures += 1
                            self._check_recovery_needed()
                            
                        return success_count > 0
                    
                    # 获取告警对应的联系人列表
                    contacts = self._get_contacts_for_alarm(warning_set_id)
                    
                    if not contacts:
                        logger.warning(f"告警类型ID {warning_set_id} 未找到对应联系人，跳过发送")
                        continue
                    
                    # 收集该组所有的告警ID
                    for alarm in alarms:
                        alarm_id = alarm.get('record_id')
                        if alarm_id:
                            all_alarm_ids.append(alarm_id)
                    
                    # 统计总联系人数
                    total_contacts += len(contacts)
                    
                    # 将告警分批处理，每批10条
                    alarm_batches = self._split_alarms_into_batches(alarms, 10)
                    logger.info(f"告警类型ID {warning_set_id} 将分{len(alarm_batches)}批发送，每批最多10条")
                    
                    batch_success_count = 0
                    group_contact_success = 0
                    
                    for batch_index, alarm_batch in enumerate(alarm_batches, 1):
                        # 检查是否超时
                        elapsed_time = (datetime.now() - start_time).total_seconds()
                        if elapsed_time > timeout_seconds:
                            logger.error(f"告警处理超时，当前处理第{batch_index}/{len(alarm_batches)}批，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                            break
                            
                        # 构建告警消息
                        batch_message = self._build_batch_message(alarm_batch, batch_index, len(alarm_batches))
                        
                        # 发送告警消息给所有联系人
                        batch_success = True
                        for contact in contacts:
                            # 检查是否超时
                            elapsed_time = (datetime.now() - start_time).total_seconds()
                            if elapsed_time > timeout_seconds:
                                logger.error(f"告警处理超时，发送给联系人时超时，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                                break
                                
                            # 设置单条消息发送超时（30秒）
                            send_start_time = datetime.now()
                            send_timeout = 30
                            
                            try:
                                # 创建一个线程来发送消息
                                send_result = [False]  # 用列表存储结果，以便在线程中修改
                                
                                def send_with_timeout():
                                    try:
                                        result = wechat_message.send_text(contact, batch_message)
                                        send_result[0] = result
                                    except Exception as e:
                                        logger.error(f"发送消息线程异常: {e}")
                                        send_result[0] = False
                                
                                # 创建并启动发送线程
                                send_thread = threading.Thread(target=send_with_timeout)
                                send_thread.daemon = True
                                send_thread.start()
                                
                                # 等待线程完成或超时
                                send_thread.join(send_timeout)
                                
                                # 检查是否超时
                                if send_thread.is_alive():
                                    logger.error(f"发送消息给{contact}超时（{send_timeout}秒）")
                                    batch_success = False
                                    continue
                                
                                # 检查发送结果
                                if send_result[0]:
                                    logger.info(f"成功发送告警类型ID {warning_set_id} 第{batch_index}批到: {contact}")
                                    # 如果是第一批，计数成功联系人
                                    if batch_index == 1:
                                        group_contact_success += 1
                                else:
                                    logger.error(f"发送告警类型ID {warning_set_id} 第{batch_index}批到 {contact} 失败")
                                    logger.error(f"发送失败内容: {batch_message}")
                                    batch_success = False
                            except Exception as e:
                                logger.error(f"发送消息处理异常: {e}")
                                batch_success = False
                        
                        if batch_success:
                            batch_success_count += 1
                        
                        # 批次之间暂停一下，避免发送过快
                        if batch_index < len(alarm_batches):
                            time.sleep(0.5)
                    
                    # 累计成功联系人数
                    success_contacts += group_contact_success
                    
                    if batch_success_count > 0:
                        success_count += 1
                    
                    # 组之间暂停一下，避免发送过快
                    time.sleep(0.5)
                
                # 记录处理过的最大告警ID，无论发送是否成功
                if all_alarm_ids:
                    max_alarm_id = max(all_alarm_ids)
                    self._add_sent_alarm(current_date, max_alarm_id)
                    logger.info(f"已记录当日最大告警ID: {max_alarm_id}")
                
                # 记录结束时间
                end_time = datetime.now()
                elapsed_time = (end_time - start_time).total_seconds()
                logger.info(f"完成告警检测和发送: {end_time.strftime('%Y-%m-%d %H:%M:%S')}, 耗时: {elapsed_time}秒")
                logger.info(f"成功发送 {success_count}/{total_groups} 组告警，联系人成功率: {success_contacts}/{total_contacts}")
                
                # 清理旧记录
                self._cleanup_old_records()
                
                # 如果成功发送了部分告警，重置失败计数
                if success_count > 0:
                    self.consecutive_failures = 0
                else:
                    self.consecutive_failures += 1
                    self._check_recovery_needed()
                
                return success_count > 0
            finally:
                # 确保任务结束后关闭数据库连接
                mysql_manager.close()
        except Exception as e:
            logger.error(f"检测告警异常: {e}")
            # 确保异常情况下也关闭数据库连接
            mysql_manager.close()
            
            # 增加失败计数并检查是否需要恢复
            self.consecutive_failures += 1
            self._check_recovery_needed()
            
            return False
            
    def _check_recovery_needed(self):
        """检查是否需要执行恢复操作"""
        # 如果连续失败次数达到阈值，执行恢复操作
        if self.consecutive_failures >= self.max_failures:
            # 检查距离上次恢复时间是否超过1小时
            current_time = time.time()
            hours_since_recovery = (current_time - self.last_recovery_time) / 3600
            
            if hours_since_recovery > 1:  # 至少间隔1小时才执行恢复
                logger.warning(f"检测到连续{self.consecutive_failures}次失败，将通过重启程序来恢复系统")
                
                # 重置微信实例 (现在会直接重启程序)
                self._reset_wechat_instance()
                
                # 注意：由于程序会重启，以下代码实际上不会执行
                # 但为了代码完整性，仍然保留
                self.last_recovery_time = current_time
                self.consecutive_failures = 0
                
                logger.info("恢复操作已触发，程序将重启")
            else:
                logger.warning(f"距离上次恢复操作仅过去{hours_since_recovery:.2f}小时，暂不执行恢复")
        else:
            # 记录当前失败次数，便于监控
            if self.consecutive_failures > 0:
                logger.info(f"当前连续失败次数: {self.consecutive_failures}/{self.max_failures}")
    
    def _group_alarms_by_warning_set_id(self, alarms: List[Dict[str, Any]]) -> Dict[int, List[Dict[str, Any]]]:
        """
        按warning_set_id分组告警
        
        Args:
            alarms: 告警列表
            
        Returns:
            Dict[int, List[Dict[str, Any]]]: 按warning_set_id分组的告警字典
        """
        grouped = {}
        for alarm in alarms:
            warning_set_id = alarm.get('warning_set_id')
            if warning_set_id is None:
                logger.warning(f"告警ID {alarm.get('record_id')} 没有warning_set_id，跳过分组")
                continue
                
            if warning_set_id not in grouped:
                grouped[warning_set_id] = []
            
            grouped[warning_set_id].append(alarm)
        
        return grouped
    
    def _build_batch_message(self, alarms: List[Dict[str, Any]], batch_index: int, total_batches: int) -> str:
        """
        构建批量告警消息
        
        Args:
            alarms: 告警列表
            batch_index: 批次索引
            total_batches: 总批次数
            
        Returns:
            str: 批量告警消息
        """
        if total_batches > 1:
            message = f"[第{batch_index}/{total_batches}批] "
        else:
            message = ""
        
        # 获取warning_set_id，用于查询description
        if alarms and len(alarms) > 0:
            warning_set_id = alarms[0].get('warning_set_id')
            description = self._get_warning_description(warning_set_id)
            if description:
                # 使用description替换原有的第一行
                message += f"{description}\n"
            else:
                # 如果未获取到description，则使用默认消息
                message += f"检测到{len(alarms)}条告警:\n"
        else:
            message += f"检测到{len(alarms)}条告警:\n"
        
        message += "-" * 30 + "\n"
        
        # 添加序号到每条告警内容
        for i, alarm in enumerate(alarms, 1):
            # message += f"{i}. {alarm.get('content', '')}\n"
            message += f"{i + (batch_index - 1) * 10}. {alarm.get('content', '')}\n"
        
        message += "-" * 30 + "\n"
        # 添加发送时间
        message += f"发送时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        
        return message
    
    def _get_warning_description(self, warning_set_id: Optional[int]) -> Optional[str]:
        """
        根据warning_set_id获取description
        
        Args:
            warning_set_id: 告警类型ID
            
        Returns:
            Optional[str]: description字段值，如果未找到则返回None
        """
        if warning_set_id is None:
            logger.warning("告警类型ID为空，无法获取description")
            return None
        
        try:
            # 查询t_warning_set表获取description
            sql = """
            SELECT description 
            FROM t_warning_set
            WHERE warning_set_id = %s
            """
            
            results = mysql_manager.query(sql, warning_set_id)
            
            if not results or not results[0].get('description'):
                logger.warning(f"未找到告警类型ID {warning_set_id} 对应的description")
                return None
            
            description = results[0].get('description')
            logger.debug(f"告警类型ID {warning_set_id} 对应的description: {description}")
            
            return description
        except Exception as e:
            logger.error(f"获取告警description异常: {e}")
            return None
    
    def _get_contacts_for_alarm(self, warning_set_id: Optional[int]) -> List[str]:
        """
        根据告警ID获取对应的联系人列表
        
        Args:
            warning_set_id: 告警类型ID
            
        Returns:
            List[str]: 联系人列表
        """
        if warning_set_id is None:
            logger.warning("告警类型ID为空，无法获取联系人")
            return []
        
        try:
            # 联表查询获取联系人
            sql = """
            SELECT ws.wx_group_name 
            FROM t_warning_wx ww
            JOIN t_wx_set ws ON ww.wx_set_id = ws.wx_set_id
            WHERE ww.warning_set_id = %s
            """
            
            results = mysql_manager.query(sql, warning_set_id)
            
            if not results:
                logger.warning(f"未找到告警类型ID {warning_set_id} 对应的联系人")
                return []
            
            contacts = [result.get('wx_group_name') for result in results if result.get('wx_group_name')]
            logger.debug(f"告警类型ID {warning_set_id} 对应的联系人: {contacts}")
            
            return contacts
        except Exception as e:
            logger.error(f"获取告警联系人异常: {e}")
            return []
    
    def run_task_now(self) -> Dict[str, Any]:
        """
        立即执行告警检测任务
        
        Returns:
            Dict[str, Any]: 执行结果
        """
        try:
            # 记录开始时间
            start_time = datetime.now()
            logger.info(f"开始手动检测告警: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
            
            # 设置任务超时时间（10分钟）
            timeout_seconds = 600
            
            # 连接数据库
            if not mysql_manager.connect():
                logger.error("数据库连接失败")
                return {"success": False, "message": "数据库连接失败"}
            
            try:
                # 查询最新告警
                table_name = config_manager.get_value("database", "table_name", "t_warning_info")
                # 修改为查询当天的所有告警
                current_date = datetime.now().strftime("%Y-%m-%d")
                
                sql = f"""
                SELECT * FROM {table_name}
                WHERE DATE(create_time) = '{current_date}'
                ORDER BY create_time DESC
                """
                results = mysql_manager.query(sql)
                
                if not results:
                    logger.info("未发现符合条件的今日告警")
                    return {"success": True, "message": "未发现符合条件的今日告警", "total_alarms": 0, "new_alarms": 0, "sent_groups": 0, "total_groups": 0, "contact_success": 0}
                
                # 重新加载已发送告警记录，确保获取最新状态
                self.sent_alarms = self._load_sent_alarms()
                
                # 过滤掉已发送的告警（如果不重复发送）
                alarms_to_send = results
                total_alarms = len(results)
                new_alarms_count = 0
                
                if not self.resend_daily_alarms:
                    new_alarms = []
                    max_sent_id = self.sent_alarms.get(current_date, 0)
                    logger.info(f"当前日期{current_date}的最大已发送record_id为{max_sent_id}")
                    
                    for alarm in results:
                        alarm_id = alarm.get('record_id')
                        if alarm_id and alarm_id > max_sent_id:
                            new_alarms.append(alarm)
                    
                    # 如果没有新告警，直接返回
                    if not new_alarms:
                        logger.info("今日告警已全部发送")
                        return {"success": True, "message": "今日告警已全部发送", "total_alarms": total_alarms, "new_alarms": 0, "sent_groups": 0, "total_groups": 0, "contact_success": 0}
                    
                    alarms_to_send = new_alarms
                    new_alarms_count = len(new_alarms)
                    logger.info(f"发现{new_alarms_count}条新告警")
                else:
                    # 如果重复发送，仍然记录已发送告警，但不过滤
                    new_alarms_count = total_alarms
                    logger.info(f"发现{total_alarms}条告警(包含已发送)")
                
                # 按warning_set_id分组告警
                grouped_alarms = self._group_alarms_by_warning_set_id(alarms_to_send)
                logger.info(f"告警按warning_set_id分组后共{len(grouped_alarms)}组")
                
                # 发送告警消息
                success_count = 0
                total_groups = len(grouped_alarms)
                total_contacts = 0  # 总联系人数
                success_contacts = 0  # 成功发送的联系人数
                
                # 追踪所有告警ID，无论发送是否成功都要记录
                all_alarm_ids = []
                
                for warning_set_id, alarms in grouped_alarms.items():
                    # 检查是否超时
                    elapsed_time = (datetime.now() - start_time).total_seconds()
                    if elapsed_time > timeout_seconds:
                        logger.error(f"告警处理超时，已处理{success_count}/{total_groups}组，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                        # 记录已处理的最大告警ID
                        if all_alarm_ids:
                            max_alarm_id = max(all_alarm_ids)
                            self._add_sent_alarm(current_date, max_alarm_id)
                            logger.info(f"已记录当日已处理的最大告警ID: {max_alarm_id}")
                        return {
                            "success": success_count > 0,
                            "message": f"告警处理超时，已处理{success_count}/{total_groups}组",
                            "total_alarms": total_alarms,
                            "new_alarms": new_alarms_count,
                            "sent_groups": success_count,
                            "total_groups": total_groups,
                            "contact_success": success_contacts
                        }
                    
                    # 获取告警对应的联系人列表
                    contacts = self._get_contacts_for_alarm(warning_set_id)
                    
                    if not contacts:
                        logger.warning(f"告警类型ID {warning_set_id} 未找到对应联系人，跳过发送")
                        continue
                    
                    # 收集该组所有的告警ID
                    for alarm in alarms:
                        alarm_id = alarm.get('record_id')
                        if alarm_id:
                            all_alarm_ids.append(alarm_id)
                    
                    # 统计总联系人数
                    total_contacts += len(contacts)
                    
                    # 将告警分批处理，每批10条
                    alarm_batches = self._split_alarms_into_batches(alarms, 10)
                    logger.info(f"告警类型ID {warning_set_id} 将分{len(alarm_batches)}批发送，每批最多10条")
                    
                    batch_success_count = 0
                    group_contact_success = 0  # 该组成功发送的联系人数
                    
                    for batch_index, alarm_batch in enumerate(alarm_batches, 1):
                        # 检查是否超时
                        elapsed_time = (datetime.now() - start_time).total_seconds()
                        if elapsed_time > timeout_seconds:
                            logger.error(f"告警处理超时，当前处理第{batch_index}/{len(alarm_batches)}批，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                            break
                            
                        # 构建告警消息
                        batch_message = self._build_batch_message(alarm_batch, batch_index, len(alarm_batches))
                        
                        # 发送告警消息给所有联系人
                        batch_success = True
                        for contact in contacts:
                            # 检查是否超时
                            elapsed_time = (datetime.now() - start_time).total_seconds()
                            if elapsed_time > timeout_seconds:
                                logger.error(f"告警处理超时，发送给联系人时超时，耗时{elapsed_time}秒，超过{timeout_seconds}秒限制")
                                break
                                
                            # 设置单条消息发送超时（30秒）
                            send_start_time = datetime.now()
                            send_timeout = 30
                            
                            try:
                                # 创建一个线程来发送消息
                                send_result = [False]  # 用列表存储结果，以便在线程中修改
                                
                                def send_with_timeout():
                                    try:
                                        result = wechat_message.send_text(contact, batch_message)
                                        send_result[0] = result
                                    except Exception as e:
                                        logger.error(f"发送消息线程异常: {e}")
                                        send_result[0] = False
                                
                                # 创建并启动发送线程
                                send_thread = threading.Thread(target=send_with_timeout)
                                send_thread.daemon = True
                                send_thread.start()
                                
                                # 等待线程完成或超时
                                send_thread.join(send_timeout)
                                
                                # 检查是否超时
                                if send_thread.is_alive():
                                    logger.error(f"发送消息给{contact}超时（{send_timeout}秒）")
                                    batch_success = False
                                    continue
                                
                                # 检查发送结果
                                if send_result[0]:
                                    logger.info(f"成功发送告警类型ID {warning_set_id} 第{batch_index}批到: {contact}")
                                    # 如果是第一批，计数成功联系人
                                    if batch_index == 1:
                                        group_contact_success += 1
                                else:
                                    logger.error(f"发送告警类型ID {warning_set_id} 第{batch_index}批到 {contact} 失败")
                                    logger.error(f"发送失败内容: {batch_message}")
                                    batch_success = False
                            except Exception as e:
                                logger.error(f"发送消息处理异常: {e}")
                                batch_success = False
                        
                        if batch_success:
                            batch_success_count += 1
                        
                        # 批次之间暂停一下，避免发送过快
                        if batch_index < len(alarm_batches):
                            time.sleep(0.5)
                    
                    # 累计成功联系人数
                    success_contacts += group_contact_success
                    
                    if batch_success_count > 0:
                        success_count += 1
                    
                    # 组之间暂停一下，避免发送过快
                    time.sleep(0.5)
                
                # 记录处理过的最大告警ID，无论发送是否成功
                if all_alarm_ids:
                    max_alarm_id = max(all_alarm_ids)
                    self._add_sent_alarm(current_date, max_alarm_id)
                    logger.info(f"已记录当日最大告警ID: {max_alarm_id}")
                
                # 记录结束时间
                end_time = datetime.now()
                elapsed_time = (end_time - start_time).total_seconds()
                logger.info(f"完成手动告警检测和发送: {end_time.strftime('%Y-%m-%d %H:%M:%S')}, 耗时: {elapsed_time}秒")
                logger.info(f"成功发送 {success_count}/{total_groups} 组告警，联系人成功率: {success_contacts}/{total_contacts}")
                
                return {
                    "success": success_count > 0,
                    "message": f"成功发送 {success_count}/{total_groups} 组告警，联系人成功率: {success_contacts}/{total_contacts}",
                    "total_alarms": total_alarms,
                    "new_alarms": new_alarms_count,
                    "sent_groups": success_count,
                    "total_groups": total_groups,
                    "contact_success": success_contacts
                }
            finally:
                # 确保任务结束后关闭数据库连接
                mysql_manager.close()
        except Exception as e:
            logger.error(f"手动检测告警异常: {e}")
            # 确保异常情况下也关闭数据库连接
            mysql_manager.close()
            return {"success": False, "message": f"发生异常: {e}", "total_alarms": 0, "new_alarms": 0, "sent_groups": 0, "total_groups": 0, "contact_success": 0}

    def test_restart(self):
        """测试重启功能"""
        logger.info("手动触发重启测试")
        return self._restart_program()
    
    def test_restart_check(self):
        """测试重启时间检查功能"""
        logger.info("手动测试重启时间检查")
        
        # 获取当前时间
        from datetime import datetime
        now = datetime.now()
        
        # 输出当前状态
        logger.info(f"当前时间: {now.hour:02d}:{now.minute:02d}:{now.second:02d}")
        logger.info(f"目标重启时间: {self.restart_hour:02d}:{self.restart_minute:02d}")
        
        # 执行检查
        self._check_restart_time()
        
        return True

# 单例实例
task_scheduler = TaskScheduler() 